package ui.client;
import java.awt.*;
import java.awt.event.*;

import javax.swing.*;
import java.io.*;
import java.net.SocketException;

import util.FileTransferConfig;
import util.IEventCallback;
import util.IOUtil;


public class JFileTransferClientApp extends JFrame 
								implements ActionListener {
	
	private FileTransferClient client;
	private FileTransferConfig config;
	
	
	private JPanel panelStatus;
	private JTabbedPane tabPanel;
	private JTextField txSourceFile;
	private JTextField txClientHost;
	private JTextField txClientPort;
	private JButton btnChooseSrc;
	private JButton btnSendfile;
	private JButton btnPauseResume;
	private JTextArea taClientLog;
	private JProgressBar progress;
	
	private ReconnectThread reConnectThread;
	private IOUtil ioUtil;
	
	class ReconnectThread extends Thread {
		private volatile boolean start = false;
		private volatile boolean isProcessing = false;
		long timeSleep = 10000;
		ReconnectThread() {};
		ReconnectThread (long timesleep) {
			timeSleep = timesleep;
		};
		public void reconnectRequest(boolean state)	{
			start = state;
		}
		public void run() {
			while(true) {
				if(start && !isProcessing){
					isProcessing = true;
					reconnect();
					isProcessing = false;
				}
				try {
					Thread.sleep(timeSleep);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	public JFileTransferClientApp() { 
	    super("File Transfer Client");
	    
	    // load config
	    try {
			config = new FileTransferConfig();
		} catch (IOException e) {
			e.printStackTrace();
		}
		// initial UI
	    initUIComponents(); 
	} 
	
	private void initUIComponents() {
		tabPanel = new JTabbedPane();
		getContentPane().add(tabPanel, BorderLayout.CENTER);;
		initClientUI();
	}
	
	private void initClientUI() {
		JPanel panelClient = new JPanel();
		tabPanel.addTab("Client", null, panelClient, null);
		panelClient.setLayout(new BorderLayout());
		//Setting panel
		JPanel panelSetting = new JPanel();
		panelClient.add(panelSetting, BorderLayout.NORTH);
		panelSetting.setLayout(new GridBagLayout());
		GridBagConstraints c = new GridBagConstraints();
		
		JLabel labHost = new JLabel("Host:");
		c.fill = GridBagConstraints.HORIZONTAL;
		c.weightx = 1.0;
		c.gridx = 0;
		c.gridy = 0;
		c.insets = new Insets(2, 2, 2, 2);
		panelSetting.add(labHost, c);
		
		txClientHost = new JTextField(config.clienthost);
		c.fill = GridBagConstraints.HORIZONTAL;
		c.weightx = 8.0;
		c.gridx = 1;
		c.gridy = 0;
		panelSetting.add(txClientHost, c);
		
		JLabel labStatic1 = new JLabel();
		c.fill = GridBagConstraints.HORIZONTAL;
		c.weightx = 1.0;
		c.gridx = 2;
		c.gridy = 0;
		panelSetting.add(labStatic1, c);
		
		JLabel labPort = new JLabel("Port:");
		c.fill = GridBagConstraints.HORIZONTAL;
		c.weightx = 1.0;
		c.gridx = 0;
		c.gridy = 1;
		panelSetting.add(labPort, c);
		
		txClientPort = new JTextField(config.clientport);
		c.fill = GridBagConstraints.HORIZONTAL;
		c.weightx = 8.0;
		c.gridx = 1;
		c.gridy = 1;
		panelSetting.add(txClientPort, c);
		
		JLabel labStatic2 = new JLabel();
		c.fill = GridBagConstraints.HORIZONTAL;
		c.weightx = 1.0;
		c.gridx = 2;
		c.gridy = 1;
		panelSetting.add(labStatic2, c);
		
		JLabel labFile = new JLabel("File:");
		c.fill = GridBagConstraints.HORIZONTAL;
		c.weightx = 1.0;
		c.gridx = 0;
		c.gridy = 2;
		panelSetting.add(labFile, c);
		
		txSourceFile = new JTextField();
		txSourceFile.setEditable(false);
		c.fill = GridBagConstraints.HORIZONTAL;
		c.weightx = 8.0;
		c.gridx = 1;
		c.gridy = 2;
		panelSetting.add(txSourceFile, c);
		
		
		btnChooseSrc = new JButton("Browse");
		c.fill = GridBagConstraints.HORIZONTAL;
		c.weightx = 1.0;
		c.gridx = 2;
		c.gridy = 2;
		btnChooseSrc.addActionListener(this);
		btnChooseSrc.setActionCommand(IOUtil.CMD_CLIENT_CHOOSE_FILE);
		panelSetting.add(btnChooseSrc, c);
		
		panelStatus = new JPanel();
		c.fill = GridBagConstraints.HORIZONTAL;
		c.weightx = 9.0;
		c.gridx = 0;
		c.gridy = 3;
		c.gridwidth = 2;
		panelSetting.add(panelStatus, c);
			
		btnSendfile = new JButton("Send");
		c.fill = GridBagConstraints.HORIZONTAL;
		c.weightx = 1.0;
		c.gridx = 2;
		c.gridy = 3;
		c.gridwidth = 1;
		//btnSendfile.setEnabled(false); 
		btnSendfile.addActionListener(this);
		btnSendfile.setActionCommand(IOUtil.CMD_CLIENT_SEND_FILE);
		panelSetting.add(btnSendfile, c);
		
		panelStatus.setLayout(new GridBagLayout());
		panelStatus.setVisible(false);
		
		progress = new JProgressBar();
		progress.setValue(0);
		progress.setStringPainted(true);
		c.fill = GridBagConstraints.HORIZONTAL;
		c.weightx = 4.0;
		c.gridx = 0;
		c.gridy = 0;
		panelStatus.add(progress, c);
				
		btnPauseResume = new JButton("Pause");
	    c.fill = GridBagConstraints.HORIZONTAL;
		c.weightx = 1.0;
		c.gridx = 1;
		c.gridy = 0;
		btnPauseResume.setEnabled(false); 
		btnPauseResume.addActionListener(this);	    	
		btnPauseResume.setActionCommand(IOUtil.CMD_CLIENT_PAUSE);
		panelStatus.add(btnPauseResume, c);
	    
	    // log panel
	    taClientLog = new JTextArea(5,20);
	    taClientLog.setMargin(new Insets(5,5,5,5));
	    taClientLog.setEditable(false);
	    JScrollPane logScrollPane = new JScrollPane(taClientLog);
	    panelClient.add(logScrollPane, BorderLayout.CENTER);
	    
	    // Dump message
	    ioUtil = new IOUtil(taClientLog);
	}
	
	private void updateObjState(String object, boolean state)
    {
    	if (object.equalsIgnoreCase("transfer")) {
    		btnPauseResume.setEnabled(state);
    	}
    	else if(object.equalsIgnoreCase("resume")) {
    		if(state){
    			btnPauseResume.setText("Resume");
    		}
    		else {
    			btnPauseResume.setText("Pause");
    		}
    	}
    	else if(object.equalsIgnoreCase("send")) {
    		if(state) {
    			btnSendfile.setText("Send");
    			btnSendfile.setActionCommand(IOUtil.CMD_CLIENT_SEND_FILE);
    		}
    		else {
    			btnSendfile.setText("Stop");
    			btnSendfile.setActionCommand(IOUtil.CMD_CLIENT_STOP);
    		}
    	}
    }
    
    private void reconnect()
    {
    	ioUtil.dumpInfoMsg("Reconnect start....");
    	final String srcfile = txSourceFile.getText();
    	new Thread(new Runnable() { 
			public void run() { 
				client.preSendSelectedFile(srcfile, true);
            	
        }}).start();
    	
    }
    
    private void reconnectThreadRequested(boolean state) {
    	ioUtil.dumpInfoMsg("reconnectThreadRequested, set status: " + state);
    	if(reConnectThread == null){
    		reConnectThread = new ReconnectThread();
    		reConnectThread.start();
    	}
		reConnectThread.reconnectRequest(state);
	}
    
    private void preSendFile(final String srcfile, final boolean autoreume)
    {
    	new Thread(new Runnable() { 
			public void run() { 
        		client.preSendSelectedFile(srcfile, autoreume);    	
        }}).start();
    }
    
    private void onSendFile(final String srcfile, final String command, final long offset)
    {
    	new Thread(new Runnable() { 
			public void run() { 
        		client.sendSelectedFile(srcfile , command, offset);
            	
        }}).start();
    }
    
    private void onGetServerMsg()
    {
    	new Thread(new Runnable() { 
			public void run() { 
        		try {
					client.getServerMsg();
				} catch (Exception e) {
					e.printStackTrace();
				}
        }}).start();
    }
    /**
     * Create the GUI and show it.  For thread safety,
     * this method should be invoked from
     * the event dispatch thread.
     */
    private static void createAndShowGUI() {
        //Create and set up the window.
    	JFrame frame = new JFileTransferClientApp();
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        		
		// Add content to the window
		frame.pack();
		frame.setLocation(100,100);
		frame.setSize(800,450);
		frame.setVisible(true);
		
    }
    
	public static void main(String args[]) {
		//Schedule a job for the event dispatch thread:
        //creating and showing this application's GUI.
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                //Turn off metal's use of bold fonts
            	UIManager.put("swing.boldMetal", Boolean.TRUE);
            	createAndShowGUI();
            }
        });
	}

	public void actionPerformed(ActionEvent e) {
		ioUtil.dumpInfoMsg("Get action command:" + e.getActionCommand().toString());
		if (e.getActionCommand().equalsIgnoreCase(IOUtil.CMD_CLIENT_SEND_FILE)) {
			try {
				config.clienthost = txClientHost.getText();
				config.clientport = txClientPort.getText();
				config.setConfig();
				client = new FileTransferClient(config.clienthost, Integer.valueOf(config.clientport));
				client.setCallback(this);
				
			} catch (IOException ex) {
				ex.printStackTrace();
			}
			
			String srcfile = txSourceFile.getText();
			ioUtil.dumpInfoMsg("Start sending file...." + srcfile);
			// Do pre-checking before sending file
			preSendFile(srcfile, false);

		}
		else if (e.getActionCommand().equalsIgnoreCase(IOUtil.CMD_CLIENT_CHOOSE_FILE)) {
			JFileChooser fc = new JFileChooser(this.txSourceFile.getText());
			int returnVal = fc.showOpenDialog(this);

            if (returnVal == JFileChooser.APPROVE_OPTION) {
                File file = fc.getSelectedFile();
                txSourceFile.setText(file.getAbsolutePath()); 
                //This is where a real application would open the file.
                ioUtil.dumpInfoMsg("Opening: " + file.getName() + ".");
            } else {
            	ioUtil.dumpInfoMsg("Open command cancelled by user.");
            }
		}
		else if (e.getActionCommand().equalsIgnoreCase(IOUtil.CMD_CLIENT_RESUME)) {
			try {
				client.pauseRequest();
			} catch (Exception e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			btnPauseResume.setActionCommand(IOUtil.CMD_CLIENT_PAUSE);
			ioUtil.dumpInfoMsg("Send Resume command by user.");
		}
		else if (e.getActionCommand().equalsIgnoreCase(IOUtil.CMD_CLIENT_PAUSE)) {
			try {
				client.pauseRequest();
			} catch (Exception e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			btnPauseResume.setActionCommand(IOUtil.CMD_CLIENT_RESUME);
			ioUtil.dumpInfoMsg("Send Pause command by user.");
		}
		else if (e.getActionCommand().equalsIgnoreCase(IOUtil.CMD_CLIENT_STOP)) {
			try {
				client.stopRequest();
			} catch (Exception ex) {
				ex.printStackTrace();
			}
			btnSendfile.setActionCommand(IOUtil.CMD_CLIENT_SEND_FILE);
			ioUtil.dumpInfoMsg("Stop sending file....");
		}
	}
	public void doErrCallback(String event, String errMessage, long...argv) 
	{
		
		StringBuffer buffer  = new StringBuffer();
		buffer.append("[ERROR]");
		if(argv.length > 0) {
			if (argv[0] == IEventCallback.ERR_IO_EXCEPTION) {
				buffer.append("[IOException]: ");
			}
			else if (argv[0] == IEventCallback.ERR_SOCKET_EXCEPTION) {
				buffer.append("[SocketException]: ");
			}
		}
		buffer.append(errMessage);
		ioUtil.dumpClientMsg(errMessage);
		// Start re-connection thread
		this.reconnectThreadRequested(true);
	}
	public void doEvtCallback(String event, long... argv) throws Exception
	{
		StringBuffer message = new StringBuffer();
		for(int i = 0; i < argv.length; i++)
		{
			if ( i == 0) {
				long evt = argv[i];
				if(evt == IEventCallback.EVT_CLIENT_SEND_FILE_PROGRESS) {
					message.append("EVT_CLIENT_SEND_FILE_PROGRESS");
				}
				else if(evt == IEventCallback.EVT_CLIENT_SEND_FILE_PAUSE) {
					message.append("EVT_CLIENT_SEND_FILE_PAUSE");
				}
				else if(evt == IEventCallback.EVT_CLIENT_SEND_FILE_RESUME) {
					message.append("EVT_CLIENT_SEND_FILE_RESUME");
				}
				else if(evt == IEventCallback.EVT_CLIENT_SEND_FILE_START) {
					message.append("EVT_CLIENT_SEND_FILE_START");
				}
				else if(evt == IEventCallback.EVT_CLIENT_SEND_FILE_COMPLETE) {
					message.append("EVT_CLIENT_SEND_FILE_COMPLETE");
				}
				else if(evt == IEventCallback.EVT_CLIENT_SEND_FILE_STOP) {
					message.append("EVT_CLIENT_SEND_FILE_STOP");
				}
				else if(evt == IEventCallback.EVT_CLIENT_TARGET_FILE_SIZE) {
					message.append("EVT_CLIENT_TARGET_FILE_SIZE");
				}
				else if(evt == IEventCallback.EVT_CLIENT_TARGET_FILE_RESUME) {
					message.append("EVT_CLIENT_TARGET_FILE_RESUME");
				}
				else if(evt == IEventCallback.EVT_SERVER_RECV_FILE_PROGRESS) {
					message.append("EVT_SERVER_RECV_FILE_PROGRESS");
				}
				else if(evt == IEventCallback.EVT_SERVER_RECV_FILE_START) {
					message.append("EVT_SERVER_RECV_FILE_START");
				}
				else if(evt == IEventCallback.EVT_CLIENT_CONN_CLOSE_READY) {
					message.append("EVT_CLIENT_CONN_CLOSE_READY");
				}
				else {
					message.append(String.valueOf(evt));
				}
			}
			else{
				message.append(", " + String.valueOf(argv[i]));
			}
		}
		
		if(event.equalsIgnoreCase(IEventCallback.CALLBACK_TYPE_CLIENT))
		{
			if(argv.length > 0) {
				if(argv[0] == IEventCallback.EVT_CLIENT_SEND_FILE_PAUSE) {
					updateObjState("resume", true);
				}
				else if( argv[0] == IEventCallback.EVT_CLIENT_SEND_FILE_RESUME) {
					updateObjState("resume", false);
				}
				else if (argv[0] == IEventCallback.EVT_CLIENT_SEND_FILE_START) {
					updateObjState("transfer", true);
					panelStatus.setVisible(true);
					updateObjState("send", false);
					// Start thread to receive message from server
					this.onGetServerMsg();
				}
				else if (argv[0] == IEventCallback.EVT_CLIENT_SEND_FILE_COMPLETE) {
					updateObjState("send", true);
					updateObjState("transfer", false);
				}
				else if (argv[0] == IEventCallback.EVT_CLIENT_SEND_FILE_STOP) {
					updateObjState("send", true);
					updateObjState("transfer", false);
					client.closeSocket();
				}
				else if (argv[0] == IEventCallback.EVT_CLIENT_TARGET_FILE_SIZE) {
					String srcfile = txSourceFile.getText();
					long offset = argv[1];
					ioUtil.dumpClientMsg("EVT_CLIENT_TARGET_FILE_SIZE " + String.valueOf(offset));
					String command = IOUtil.CMD_RESUME;
					if (offset < 0)
						return;
					else if(offset > 0) {
						Object[] options = {"Resume",
		                        "Overwrite",
		                        "Cancel"};
						int n = JOptionPane.showOptionDialog(this,
		                    "The target file exists on server, Please select action for continue...",
		                    "Question",
		                    JOptionPane.YES_NO_CANCEL_OPTION,
		                    JOptionPane.QUESTION_MESSAGE,
		                    null,
		                    options,
		                    options[0]);
						    if (n == JOptionPane.YES_OPTION) {
						        // Start transfer with resume
						    	command = IOUtil.CMD_RESUME;
						    } else if (n == JOptionPane.NO_OPTION) {
						    	//Start transfer with overwrite and reset offest to begin
						    	command = IOUtil.CMD_OVERWRITE;
						    	offset = 0;
						    } else if (n == JOptionPane.CANCEL_OPTION) {
						        // Cancel
						    	command = IOUtil.CMD_CANCEL;
						    } 
					}
					onSendFile(srcfile, command, offset);
				}
				else if(argv[0] == IEventCallback.EVT_CLIENT_TARGET_FILE_RESUME) {
					String srcfile = txSourceFile.getText();
					long offset = argv[1];
					String command = IOUtil.CMD_RESUME;
					onSendFile(srcfile, command, offset);
				}
				else if (argv[0] == IEventCallback.EVT_SERVER_RECV_FILE_PROGRESS) {
					progress.setValue((int)argv[1]);
				}
				else if (argv[0] == IEventCallback.EVT_SERVER_RECV_FILE_START) {
					this.reconnectThreadRequested(false);
				}
				else if (argv[0] == IEventCallback.EVT_CLIENT_CONN_CLOSE_READY) {
					client.closeSocket();
				}
			}
			if(argv[0] < IEventCallback.EVT_SERVER_RECV_FILE_START)
				ioUtil.dumpClientMsg(message.toString());
			else
				ioUtil.dumpServerMsg(message.toString());
		}
	}
}



